//
//  WmcNavigatorManager.swift
//  WmcMVVM-Demo
//
//  Created by 赵新 on 2021/11/25.
//

import UIKit
import URLNavigator
import YPBedrock

// MARK: - [WmcNavigatorProtocol]

/// 两步路由注册协议
protocol WmcNavigatorProtocol where Self: UIViewController {
    /// 返回本VC与路由枚举中相绑定的枚举
    static func navigationPaths() -> [WmcNavigatorManager.Path]

    /// 需要返回一个注册的VC
    /// 自行匹配设置参数
    /// - Parameter parms: 参数
    static func willMakeNavigationVC(with parms: WmcNavigatorManager.Parms) -> UIViewController

    /// 页面即将Push出现前，需要做什么
    /// - Parameter startPushBlock: 开始执行推出
    func willPushNavigationVC(startPushBlock: @escaping YPBlock.Empty, endOperationBlock: @escaping YPBlock.Empty)

    /// 页面即将Present出现前，需要做什么
    /// - Parameter startPushBlock: 开始执行推出
    func willPresentNavigationVC(startPushBlock: @escaping YPBlock.Empty, endOperationBlock: @escaping YPBlock.Empty)
}

extension WmcNavigatorProtocol {
    /// 页面即将出现
    func willPushNavigationVC(startPushBlock: @escaping YPBlock.Empty, endOperationBlock: @escaping YPBlock.Empty) {
        startPushBlock()
    }

    /// 页面即将出现前，需要做什么
    func willPresentNavigationVC(startPushBlock: @escaping YPBlock.Empty, endOperationBlock: @escaping YPBlock.Empty) {
        startPushBlock()
    }
}

extension WmcNavigatorManager {
    /// 存储键值
    struct Key: Hashable, Equatable, RawRepresentable {
        // Lifecycle

        public init(rawValue: String) { self.rawValue = rawValue }
        internal init(_ rawValue: String) { self.init(rawValue: rawValue) }

        // Public

        public var rawValue: String
    }

    /// 传参结构体
    struct Parms {
        var path: Path
        /// Url
        var url: String
        /// Url自带的参数集合
        var values: [String: String]?
        /// 传参的集合
        var context: Any?

        /// 获取所有参数，即context为字典时，合并values与context
        func allValue() -> [String: Any]? {
            var contextDic: [String: Any] = [:]
            if let _context = context as? [String: Any],
               !_context.isEmpty
            {
                contextDic = _context
            }
            guard let values = self.values else { return contextDic }
            values.forEach { (key, value) in
                contextDic[key] = value
            }
            return contextDic
        }
    }
}

// MARK: - [WmcNavigatorManager]

/**
 两步路由管理者
 第一步：在WmcNavigationRegisterKeys文件中新增一个VC的枚举，配上字符串
 第二步：在VC里遵循协议，等待代码自动补全，实现协议
 使用：
 YPNavigationManager.push(.login, parms: ["123":"456"])
 */
open class WmcNavigatorManager {
    // Public

    /// 单例实例化
    public static let shared: WmcNavigatorManager = {
        let manager = WmcNavigatorManager()
        return manager
    }()

    // Internal

    /// 懒加载单例路由
    lazy var navigator: Navigator = {
        let navigator = Navigator()
//        navigator.delegate = self
        return navigator
    }()

    var registerQueue: DispatchQueue = .init(label: "wmc.queue.WmcNavigatorManager")

    /// 前缀
    let host = "watermarkCamera"

    /// 前缀
    var prefix: String {
        return "ypwmcios://\(host)"
    }

    @discardableResult
    static func pushCustom(_ view: WmcCustomViewProtocol)
        -> UIViewController?
    {
        let customVC = WmcCustomVC(customView: view)
        let style = view.pushTransitionStyle(.current)
        let animated = view.pushTransitionAnimated(.current)
        return shared.navigator.wmc_push(customVC,
                                         from: nil,
                                         style: style,
                                         animated: animated)
    }

    /// 类方法推出一个页面
    /// - Parameters:
    ///   - vc: VC
    ///   - context: 对象传值
    @discardableResult
    static func push(_ vc: UIViewController,
                     style: WmcTransition.Style? = nil,
                     parms: Any? = nil,
                     from: UINavigationControllerType? = nil,
                     animated: Bool = true)
        -> UIViewController?
    {
        return shared.navigator.wmc_push(vc,
                                         from: from,
                                         style: style,
                                         animated: animated)
    }

    /// 类方法推出一个页面
    /// - Parameters:
    ///   - path: 路由url
    ///   - context: 对象传值
    @discardableResult
    static func push(_ path: WmcNavigatorManager.Path,
                     style: WmcTransition.Style? = nil,
                     parms: Any? = nil,
                     from: UINavigationControllerType? = nil,
                     animated: Bool = true,
                     completion: (() -> Void)? = nil)
        -> UIViewController?
    {
        return push("\(shared.prefix)/\(path.rawValue)",
                    style: style,
                    parms: parms,
                    from: from,
                    animated: animated,
                    completion: completion)
    }

    /// 类方法推出一个页面
    /// - Parameters:
    ///   - path: path
    ///   - context: 对象传值
    @discardableResult
    static func push(_ path: String,
                     style: WmcTransition.Style? = nil,
                     parms: Any? = nil,
                     from: UINavigationControllerType? = nil,
                     animated: Bool = true,
                     completion: (() -> Void)? = nil)
        -> UIViewController?
    {
        var correctPath = path
        if !path.hasPrefix(shared.prefix) {
            correctPath = "\(shared.prefix)/\(path)"
        }
        let vc = shared.navigator.wmc_push(correctPath,
                                           context: parms,
                                           from: from,
                                           style: style,
                                           animated: animated,
                                           completion: completion)
        return vc
    }

    /// 类方法拉起
    /// - Parameters:
    ///   - path: 路由url
    ///   - context: 对象传值
    ///   - wrap: UINavigationController
    ///   - from: UINavigationControllerType
    ///   - animated: 动画
    ///   - completion: 完成回调
    @discardableResult
    static func present(_ path: WmcNavigatorManager.Path,
                        parms: Any? = nil,
                        wrap: UINavigationController.Type? = nil,
                        from: URLNavigator.UIViewControllerType? = nil,
                        animated: Bool = true,
                        completion: (() -> Void)? = nil)
        -> UIViewController?
    {
        return present("\(shared.prefix)/\(path)",
                       parms: parms,
                       wrap: wrap,
                       from: from,
                       animated: animated,
                       completion: completion)
    }

    /// 类方法拉起
    /// - Parameters:
    ///   - path: 路由url
    ///   - context: 对象传值
    ///   - wrap: UINavigationController
    ///   - from: UINavigationControllerType
    ///   - animated: 动画
    ///   - completion: 完成回调
    @discardableResult
    static func present(_ path: String,
                        parms: Any? = nil,
                        wrap: UINavigationController.Type? = nil,
                        from: URLNavigator.UIViewControllerType? = nil,
                        animated: Bool = true,
                        completion: (() -> Void)? = nil)
        -> UIViewController?
    {
        var correctPath = path
        if !path.hasPrefix(shared.prefix) {
            correctPath = "\(shared.prefix)/\(path)"
        }
        let vc = shared.navigator.wmc_present(correctPath,
                                              context: parms,
                                              wrap: wrap,
                                              from: from,
                                              animated: animated,
                                              completion: completion)
        return vc
    }

    /// 配置新业务路由
    func config() {
        registerPage() // 注册所有页面
    }

    /// 日志系统
    func log(_ log: String) {
        WmcLogManager.DebugLog(type: .navigatorManager, log: log)
    }

    // Private

    /// 页面注册
    private func registerPage() {
        registerQueue.async { [weak self] in
            guard let weakSelf = self else { return }
            #if DEBUG
                let startTime = CFAbsoluteTimeGetCurrent()
            #endif
            // 通过遍历Path中所有类，检测是否遵循YPNavigationProtocol协议
            // 如果遵循，则将该类注册进路由管理
            let allCases = Path.allCases
            for cases in allCases {
                if let _class = cases.info.ClassType as? WmcNavigatorProtocol.Type { // 符合路由协议的类
                    let paths = _class.navigationPaths() // 获取该类所有注册路径
                    for enu in paths { // 遍历注册
                        weakSelf.registerNavigator(navObj: _class, pathEnum: enu)
                    }
                }
            }
            #if DEBUG
                let endTime = CFAbsoluteTimeGetCurrent()
                let performTime = (endTime - startTime) * 1000
                weakSelf.log("路由注册完成！共计\(allCases.count)条路由，注册总耗时：\(performTime)毫秒")
            #endif
        }
    }

    /// 注册路由
    /// - Parameter pathEnum: 路由枚举
    private func registerNavigator(navObj: WmcNavigatorProtocol.Type,
                                   pathEnum: Path)
    {
        let registerUrl = "\(prefix)/\(pathEnum.rawValue)"
        log("正在注册路由：URL:'\(registerUrl)'VC:[\(navObj)]")
        navigator.register(registerUrl) { (url, value, context) -> UIViewController? in
            // 触发跳转事件
            var paramsMap: [String: String] = [:]
            let url_paramStr = "\(url)".components(separatedBy: "?")
            if url_paramStr.count == 2, // 前后
               let paramStr = url_paramStr.yp.second, // 参数字段有
               paramStr.count > 0 // 且长度大于0
            {
                let keyValues = paramStr.components(separatedBy: "&")
                keyValues.forEach { kvStr in
                    let kvList = kvStr.components(separatedBy: "=")
                    if kvList.count == 2, // 完整键值对
                       let key = kvList.first,
                       let value = kvList.last
                    {
                        paramsMap[key] = value
                    }
                }
            }
            let parms = Parms(path: pathEnum,
                              url: url.urlStringValue,
                              values: paramsMap,
                              context: context)
            return navObj.willMakeNavigationVC(with: parms)
        }
    }
}
